package com.dyx.xml.sax;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.FileReader;
import java.io.InputStream;

/**
 *
 * SAX(Simple API for XML)解析器采用了基于事件的模型，它在解析XML文档的时候可以触发一系列的事件，当发现给定的tag的时候，
 * 它可以激活一个回调方法，告诉该方法制定的标签已经找到。SAX对内存的要求通常会比较低，因为它让开发人员自己来决定所要处理的tag.
 * 特别是当开发人员只需要处理文档中所包含的部分数据时，SAX这种扩展能力得到了更好的体现。但用SAX解析器的时候编码工作会比较困难，
 * 而且很难同时访问同一个文档中的多处不同数据。
 *
 * 【优势】
 * ①不需要等待所有数据都被处理，分析就能立即开始。
 * ②只在读取数据时检查数据，不需要保存在内存中。
 * ③可以在某个条件得到满足时停止解析，不必解析整个文档。
 * ④效率和性能较高，能解析大于系统内存的文档。
 * 不用事先调入整个文档，占用资源少；SAX解析器代码比DOM解析器代码小，适于Applet，下载。
 *
 * 【缺点】
 *
 * ①需要应用程序自己负责TAG的处理逻辑（例如维护父/子关系等），文档越复杂程序就越复杂。
 * ②单向导航，无法定位文档层次，很难同时访问同一文档的不同部分数据，不支持XPath。
 * 不是持久的；事件过后，若没保存数据，那么数据就丢了；无状态性；从事件中只能得到文本，但不知该文本属于哪个元素；
 * 使用场合：Applet;只需XML文档的少量内容，很少回头访问；机器内存少。
 *
 * 【原理】
 * 		简单的说就是对文档进行顺序扫描，当扫描到文档(document)开始与结束、元素(element)开始与结束时通知事件
 * 		处理函数(回调函数)，进行相应处理，直到文档结束
 * 	【事件处理器类型】
 * 		①访问XML DTD：DTDHandler
 * 		②低级访问解析错误：ErrorHandler
 * 		③访问文档内容：ContextHandler
 * 	【DefaultHandler类】
 * 		SAX事件处理程序的默认基类，实现了DTDHandler、ErrorHandler、ContextHandler和EntityResolver接口，通常
 * 		做法是，继承该基类，重写需要的方法，如startDocument()
 * 	【创建SAX解析器】
 * 		SAXParserFactory saxf = SAXParserFactory.newInstance();
 * 		SAXParser sax = saxf.newSAXParser();
 * 	注：关于遍历
 * 		①深度优先遍历(Depthi-First Traserval)
 * 		②广度优先遍历(Width-First Traserval)
 * @param:
 * @return: 
 *
 * @author: dengyouxu
 * @date: 2020/4/1 10:58 上午
 *
 * https://www.cnblogs.com/zjm-1/p/9350884.html
 *
 */
public class ParseXmlWithSAX {

    /**
     * sax simple API for XML，现在有两个版本，sax和sax2。
     *
     * sax不支持LocalName、QName和uri。对于属性sina:blog="blog.sina.com"，sax解析的结果是LocalName=QName="sina:blog"，uri=""，value="blog.sina.com"。
     * sax2支持LocalName、QName、uri。对于属性sina:blog="blog.sina.com"，sax2解析的结果是LocalName="blog"，QName="sina:blog"，uri=""，value="blog.sina.com"。

     *
     * @param args args
     * @throws Exception exception
     * @author dengyouxu
     * @date 2021.06.11 14:24:47:006
     */
    public static void main(String[] args) throws Exception {
//        parseXMLWithSAX1("demo.xml");
//        parseXMLWithSAX2("demo.xml");
        parseXMLWithSAX1("complexDemo.xml");
        System.out.println("================================== 我是分割线 ==================================");
        System.out.println("==================================          ==================================");
        System.out.println("==================================          ==================================");
        System.out.println("==================================          ==================================");
        System.out.println("==================================          ==================================");
        System.out.println("==================================          ==================================");
        System.out.println("================================== 我是分割线 ==================================");
        parseXMLWithSAX2("complexDemo.xml");

    }

    //sax1解析XML文档
    private static void parseXMLWithSAX1(String xmlFileName) throws Exception {
        System.out.println("parse xml file use sax1");

        //1.或去SAXParserFactory实例
        SAXParserFactory factory = SAXParserFactory.newInstance();
        //2.获取SAXparser实例
        SAXParser saxParser = factory.newSAXParser();
        //创建Handel对象
        SAXDemoHandler handler = new SAXDemoHandler();
        InputStream resourceAsStream = ParseXmlWithSAX.class.getClassLoader().getResourceAsStream(xmlFileName);

        saxParser.parse(resourceAsStream, handler);
    }

    //Sax2解析XML文档
    private static void parseXMLWithSAX2(String xmlFileName) throws Exception {

        System.out.println("parse xml file use sax2");
        SAXDemoHandler handler = new SAXDemoHandler();
        XMLReader xmlReader = XMLReaderFactory.createXMLReader();
        xmlReader.setContentHandler(handler);
        xmlReader.setErrorHandler(handler);

        InputStream resourceAsStream = ParseXmlWithSAX.class.getClassLoader().getResourceAsStream(xmlFileName);
        xmlReader.parse(new InputSource(resourceAsStream));

    }

}